/*
 * Copyright (c) 2022. All rights reserved.
 * Please don't try to modify the code, because I don't know what the consequences will be.
 *
 *
 *
 *
 *
 *
 *
 */

package cc.zzone.util;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;

/**
 * Mvc 工具类
 *
 * @author jinzhengze
 * @since 2023年1月30日
 */
@Slf4j
@Component
@Order(Integer.MIN_VALUE)
public class SpringMVCUtil extends OncePerRequestFilter {

    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        if (!(request instanceof ContentCachingRequestWrapper))
            request = new ContentCachingRequestWrapper(request);
        if (!(response instanceof ContentCachingResponseWrapper))
            response = new ContentCachingResponseWrapper(response);
        filterChain.doFilter(request, response);
        ((ContentCachingResponseWrapper) response).copyBodyToResponse();
    }

    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("Proxy-Client-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("WL-Proxy-Client-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getRemoteAddr();
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("http_client_ip");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        // 如果是多级代理，那么取第一个 ip 为客户 ip
        if (ip != null && ip.contains(","))
            ip = ip.substring(ip.lastIndexOf(",") + 1).trim();
        return ip;
    }

    public static HttpServletRequest getRequest() {
        if (RequestContextHolder.getRequestAttributes() == null)
            return null;
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return servletRequestAttributes.getRequest();
    }

    public static HttpServletResponse getResponse() {
        if (RequestContextHolder.getRequestAttributes() == null)
            return null;
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return servletRequestAttributes.getResponse();
    }

    public static String getRequestBody(HttpServletRequest request) {
        ContentCachingRequestWrapper wrapper;
        if (request instanceof ContentCachingRequestWrapper) wrapper = (ContentCachingRequestWrapper) request;
        else wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            String s = new String(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
            return StringUtils.hasText(s) ? s : null;
        }
        return null;
    }

    public static String getResponseBody(HttpServletResponse response) {
        ContentCachingResponseWrapper wrapper;
        if (response instanceof ContentCachingResponseWrapper) wrapper = (ContentCachingResponseWrapper) response;
        else wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            String s = new String(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
            return StringUtils.hasText(s) ? s : null;
        }
        return null;
    }

    public static void setHeader(HttpServletRequest request, String name, String value) {
        try {
            if (request instanceof ServletRequestWrapper) {
                Field requestField = ServletRequestWrapper.class.getDeclaredField("request");
                requestField.setAccessible(true);
                HttpServletRequest requestTarget = (HttpServletRequest) requestField.get(request);
                if (requestTarget instanceof RequestFacade)
                    request = requestTarget;
            }
            Field requestField = RequestFacade.class.getDeclaredField("request");
            requestField.setAccessible(true);
            Request requestReal = (Request) requestField.get(request);
            Field coyoteRequestField = Request.class.getDeclaredField("coyoteRequest");
            coyoteRequestField.setAccessible(true);
            org.apache.coyote.Request coyoteRequest = (org.apache.coyote.Request) coyoteRequestField.get(requestReal);
            MimeHeaders headers = coyoteRequest.getMimeHeaders();
            headers.addValue(name).setString(value);
        } catch (Exception e) {
            log.error("Error : ", e);
        }
    }
}
